package com.timk.goserver.client.ui.screens;

import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.PopupListener;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.SourcesTabEvents;
import com.google.gwt.user.client.ui.TabBar;
import com.google.gwt.user.client.ui.TabListener;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.timk.goserver.client.model.ClientGameInfo;
import com.timk.goserver.client.model.ClientUserInfo;
import com.timk.goserver.client.services.GameService;
import com.timk.goserver.client.services.NoSessionException;
import com.timk.goserver.client.ui.widgets.DisplayUserLinkListener;
import com.timk.goserver.client.ui.widgets.RankGraphLink;
import com.timk.goserver.client.ui.widgets.StatusLabel;
import com.timk.goserver.client.ui.widgets.UserLink;

/** */
public class GamelistPanel extends Composite {	// NOPMD - Complexity
	private static final int RESULTS_PER_PAGE = 15;
	
	private static final String WHITE_STONE_IMAGE;
	private static final String BLACK_STONE_IMAGE;
	private static final String UPDOWN_IMAGE;
	private static final String WIN_IMAGE;
	private static final String LOSS_IMAGE;
	
	static {
		WHITE_STONE_IMAGE = "<img src=\"" + GWT.getModuleBaseURL() 
				+ "images/17/w.gif\"/>";
		BLACK_STONE_IMAGE = "<img src=\"" + GWT.getModuleBaseURL() 
				+ "images/17/b.gif\"/>";
		UPDOWN_IMAGE = "<img src=\"" + GWT.getModuleBaseURL() 
				+ "images/updown.gif\"/>";
		WIN_IMAGE = "<img src=\"" + GWT.getModuleBaseURL() 
				+ "images/win.gif\"/>";
		LOSS_IMAGE = "<img src=\"" + GWT.getModuleBaseURL() 
				+ "images/loss.gif\"/>";
	}
	
	private final DisplayUserLinkListener userLinkListener;
	
	final StatusLabel status; // NOPMD - Explicit scoping
	final Image loadingImage; // NOPMD - Explicit scoping
	private final FlexTable table;
	private final HorizontalPanel buttonPanel;
	private final HTML lastUpdated;
	private final Button prevPageButton;
	private final Button nextPageButton;
	
	List games; // NOPMD - Explicit scoping
	String username; // NOPMD - Explicit scoping
	int filterType; // NOPMD - Explicit scoping
	private final boolean isCurUser;
	private int startIndex = 0;
	
	GameplayDialog gameplayDialog; // NOPMD - Explicit scoping
	
	/**
	 * Creates a GamelistPanel
	 * @param mainPanel the MainPanel
	 * @param username list the games for this user
	 * @param isCurUser true if the list is for the logged in user
	 */
	public GamelistPanel(MainPanel mainPanel, String username, boolean isCurUser) {
		this.username = username;
		this.isCurUser = isCurUser;
		this.userLinkListener = new DisplayUserLinkListener(mainPanel);
		
		VerticalPanel vertPanel = new VerticalPanel();
		
		TabBar tabBar = new TabBar();
		
		if(isCurUser) {
			tabBar.addTab("My Turn");
		}
		
		tabBar.addTab("Running");
		tabBar.addTab("Finished");
		vertPanel.add(tabBar);
		tabBar.setWidth("100%");
		vertPanel.setCellWidth(tabBar, "100%");
		
		tabBar.addTabListener(new TabListener() {
			public void onTabSelected(SourcesTabEvents sender, int tabIndex) {
				GamelistPanel.this.onTabSelected(tabIndex);
			}
			
			public boolean onBeforeTabSelected(SourcesTabEvents sender, int tabIndex) {
				return true;
			}
		});
		
		loadingImage = new Image("images/brownloading.gif");
		vertPanel.add(loadingImage);
		
		table = new FlexTable();
		table.addStyleName("goserver-Table");
		vertPanel.add(table);
		
		buttonPanel = new HorizontalPanel();

		prevPageButton = new Button("&lt;");
		prevPageButton.addClickListener(new ClickListener() {
			public void onClick(Widget sender) {
				prevPage();
			}
		});
		buttonPanel.add(prevPageButton);
		
		nextPageButton = new Button("&gt;");
		nextPageButton.addClickListener(new ClickListener() {
			public void onClick(Widget sender) {
				nextPage();
			}
		});
		buttonPanel.add(nextPageButton);
		
		vertPanel.add(buttonPanel);

		status = new StatusLabel();
		vertPanel.add(status);
		vertPanel.setCellWidth(status, "100%");
		vertPanel.setCellHorizontalAlignment(status, HasHorizontalAlignment.ALIGN_CENTER);
		
		lastUpdated = new HTML();
		vertPanel.add(lastUpdated);
		vertPanel.setCellWidth(lastUpdated, "100%");
		vertPanel.setCellHorizontalAlignment(lastUpdated,
				HasHorizontalAlignment.ALIGN_CENTER);
		
		initWidget(vertPanel);
		
		gameplayDialog = new GameplayDialog(mainPanel);
		
		gameplayDialog.addPopupListener(new PopupListener() {
			public void onPopupClosed(PopupPanel panel, boolean autoClosed) {
				if(filterType == GameService.GAME_FILTER_NEXT_TO_MOVE) {
					refreshGames();
				}
			}
		});

		//	This sets the filter and calls refresh
		tabBar.selectTab(0);
	}
	
	void onTabSelected(int tabIndex) {
		if(isCurUser) {
			switch(tabIndex) {
			case 0: filterType = GameService.GAME_FILTER_NEXT_TO_MOVE; break;
			case 1: filterType = GameService.GAME_FILTER_RUNNING; break;
			case 2: filterType = GameService.GAME_FILTER_FINISHED; break;
			default: status.setError("Unknown tab index: " + tabIndex); break;
			}
		} else {
			switch(tabIndex) {
			case 0: filterType = GameService.GAME_FILTER_RUNNING; break;
			case 1: filterType = GameService.GAME_FILTER_FINISHED; break;
			default: status.setError("Unknown tab index: " + tabIndex); break;
			}
		}
		
		startIndex = 0;
		refreshGames();
	}
	
	void prevPage() {
		startIndex -= RESULTS_PER_PAGE;
		if(startIndex < 0) {
			startIndex = 0;
		}
		
		refreshGames();
	}
	
	void nextPage() {
		startIndex += RESULTS_PER_PAGE;
		
		refreshGames();
	}
	
	/**
	 * Refreshes the game list
	 *
	 */
	public final void refreshGames() {
		int numGames = filterType == GameService.GAME_FILTER_NEXT_TO_MOVE ?
				Integer.MAX_VALUE : RESULTS_PER_PAGE;
		loadingImage.setVisible(true);
		
		MainPanel.gameService.getGames(username, filterType, startIndex,
				numGames, new AsyncCallback() {
			public void onSuccess(Object result) {
				loadingImage.setVisible(false);
				status.clear();
				fillTable((List)result);
			}
			
			public void onFailure(Throwable caught) {
				loadingImage.setVisible(false);
				
				if(caught instanceof NoSessionException) {
					MainPanel.sessionExpired(caught.getMessage());
					return;
				}
				
				status.setError("Couldn't retrieve games");
			}
		});
	}
	
	final void fillTable(List loadedGames) {	// NOPMD - Complexity
		if(loadedGames.size() == 0 && startIndex != 0)
		{
			startIndex -= RESULTS_PER_PAGE;
			nextPageButton.setEnabled(false);
			return;
		}
		
		games = loadedGames;
		
		int numRows = table.getRowCount();
		for(int i = 0; i < numRows; i++) {
			table.removeRow(0);
		}
		
		Date d = new Date();
		lastUpdated.setHTML("<small>Last Updated: " + d.toString() + "</small>");
		lastUpdated.setVisible(true);
		
		if(games.size() == 0) {
			status.setInfo("No games found");
			table.setVisible(false);
			buttonPanel.setVisible(false);
			return;
		}
		
		boolean canPlay = filterType == GameService.GAME_FILTER_NEXT_TO_MOVE;
		
		if(canPlay) {
			Collections.sort(games, new LastMoveComparator(false));
		} else {
			Collections.sort(games, new LastMoveComparator(true));
		}
		
		status.clear();
		table.setVisible(true);
		buttonPanel.setVisible(true);
		
		int col = 0;
		table.setHTML(0, col++, "<b>ID</b>");
		table.setHTML(0, col++, "<b>Opponent</b>");
		table.setHTML(0, col++, "<b>Size</b>");
		table.setHTML(0, col++, "<b>Color</b>");
		table.setHTML(0, col++, "<b>Handicap</b>");
		table.setHTML(0, col++, "<b>Komi</b>");
		table.setHTML(0, col++, "<b>Opp. Rating</b>");
		table.setHTML(0, col++, "<b>Moves</b>");
		table.setHTML(0, col++, "<b>Rated</b>");
		
		if(filterType == GameService.GAME_FILTER_FINISHED) {
			table.setHTML(0, col++, "<b>Result</b>");
			table.setHTML(0, col++, UPDOWN_IMAGE);
		}
		
		table.setHTML(0, col++, "<b>SGF</b>");
		
		int curRow = 1;
		for(int i = 0; i < games.size(); i++) {
			ClientGameInfo game = (ClientGameInfo)games.get(i);

			col = 0;
			table.setText(curRow, col++, String.valueOf(game.getGameId()));
			
			int thisPlayer = findThisPlayer(game);
			int otherPlayerIndex = thisPlayer % 2 == 0 ? 1 : 0;
			ClientUserInfo [] players = game.getPlayers();
			
			if(players.length < 3) {
				UserLink userLink = new UserLink(null,
						players[otherPlayerIndex].getUsername(),
						userLinkListener);
				table.setWidget(curRow, col++, userLink);
			} else {
				String names = players[otherPlayerIndex].getUsername() +
					"<br/>" + players[otherPlayerIndex + 2].getUsername();
				table.setHTML(curRow, col++, names);
			}
			
			table.setText(curRow, col++, String.valueOf(game.getSize()));
			
			int color = game.getColor(username);
			table.setHTML(curRow, col++, color == 1 ?
					BLACK_STONE_IMAGE : WHITE_STONE_IMAGE);
			
			table.setText(curRow, col++, String.valueOf(game.getHandicap()));
			table.setText(curRow, col++, String.valueOf(game.getKomi()));
			
			if(players.length < 3) {
				ClientUserInfo otherPlayer = players[otherPlayerIndex];
				RankGraphLink graphLink =
					new RankGraphLink(otherPlayer.getUsername());
				graphLink.setHTML("<u>" + otherPlayer.getRank() + "</u>");
				table.setWidget(curRow, col++, graphLink);
			} else {
				String rankStr = players[otherPlayerIndex].getRank() +
						"<br/>" + players[otherPlayerIndex + 2].getRank();
				table.setHTML(curRow, col++, rankStr);
			}
			
			table.setText(curRow, col++, String.valueOf(game.getMoveNumber() + 1));
			table.setText(curRow, col++, game.isRated() ? "Y" : "N");
			
			if(filterType == GameService.GAME_FILTER_FINISHED) {
				table.setText(curRow, col++, game.getResult());
				
				char winner = game.getResult().charAt(0);
				if((winner == 'B' && color == 1) ||
					(winner == 'W' && color == -1)) {
					table.setHTML(curRow, col++, WIN_IMAGE);
				} else {
					table.setHTML(curRow, col++, LOSS_IMAGE);
				}
			}
			
			table.setHTML(curRow, col++, "<a href=\"DownloadGame?id="
					+ game.getGameId() + "\">sgf</a>");
			
			Button playButton = new Button();
			if(canPlay) {
				playButton.setText("Play");
			} else {
				playButton.setText("View");
			}
			playButton.addClickListener(new ViewGameListener(i, canPlay));
			table.setWidget(curRow, col++, playButton);

			curRow++;
		}

		nextPageButton.setEnabled(table.getRowCount() > RESULTS_PER_PAGE &&
				filterType != GameService.GAME_FILTER_NEXT_TO_MOVE);
		prevPageButton.setEnabled(startIndex > 0);
	}
	
	//	TODO: move/get rid of findPlayer(s)?
	private int findThisPlayer(ClientGameInfo game) {
		ClientUserInfo [] players = game.getPlayers();
		
		for(int i = 0; i < players.length; i++) {
			String name = players[i].getUsername();
			if(username.equals(name)) {
				return i;
			}
		}
		
		return -1;
	}
	
	private class ViewGameListener implements ClickListener {
		private final int gameIndex;
		private final boolean canPlay;
		
		ViewGameListener(int index, boolean canPlay) {
			gameIndex = index;
			this.canPlay = canPlay;
		}

		public void onClick(Widget sender) {
			gameplayDialog.showGames(games, gameIndex, canPlay);
		}
	}
	
	private static class LastMoveComparator implements Comparator {
		private final boolean invert;
		
		/**
		 * Creates a LastMoveComparator
		 * @param invert true to invert the natural order of dates
		 */
		public LastMoveComparator(boolean invert) {
			this.invert = invert;
		}
		
		public int compare(Object arg0, Object arg1) {
			ClientGameInfo game1 = (ClientGameInfo)arg0;
			ClientGameInfo game2 = (ClientGameInfo)arg1;
			int diff = game1.getLastMoveTime().compareTo(game2.getLastMoveTime());
			return invert ? diff * -1 : diff;
		}
	}
}
